package com.bitsfromspace.ubertorrent.tracker;

import java.io.IOException;
import java.net.MalformedURLException;

import com.bitsfromspace.ubertorrent.Sha1Hash;
import com.bitsfromspace.ubertorrent.tracker.TorrentRequest.RequestEvent;

/**
 * 
 * Tracker stores the state of a tracker and the last announce attempt.
 * <br/><br/>
 * {@link #getLastAnnounceDate()} returns the date of the last announce attempt, 
 * {@link #isWasAnnounceSuccessfull()} specifies whether the last announce was successfull and
 * {@link #getNextAnnounceDate()} is the date of the last annouce + interval. 
 * <br/>
 * {@link #getUrl()} returns the tracker's URL (either HTTP or UDP).<br/><br/>
 * After each announce attempt, {@link #setAnnounced(boolean, int)} should be invoked
 * specifying whether the annouce was successful, and when the next annouce should
 * take place.  If the announce was unsuccessful,
	 * use some arbitrary value for the next announce (interval).
 * @author: chris.oudeman
 * @since: Jan 5, 2011
 */
public class Tracker {

	private final String url;

	private long lastAnnounceDate;
	private boolean wasLastAnnounceSuccess;
	private long nextAnnounceDate;
	private String trackerId;

	/**
	 * Creates a new {@link Tracker}.
	 * Throws a {@link MalformedURLException} when parameter <em> url </em>
	 * does not appear to be a valid HTTP or UDP url.
	 * @param url - The URL of the tracker, should be either in HTTP or UDP.
	 * @throws MalformedURLException When url does not look like a valid HTTP or UDP url.
	 */
	public Tracker(String url) throws MalformedURLException {

		if (!url.startsWith("http://") && !url.startsWith("udp://")) {
			throw new MalformedURLException(
					"Tracker url should start either with 'http://' or 'udp://': "
							+ url);

		}
		this.url = url;
	}

	/**
	 * Updates the last announce state: {@link #getLastAnnounceDate()} and {@link #isWasAnnounceSuccessfull()}.
	 * Parameter<em>intervalInSeconds</em> refers to the trackers response parameter interval that
	 * specifies how many seconds to wait until the next announce.  If the announce was unsuccessful,
	 * use some arbitrary value.
	 * 
	 * @param success Whether the announce was successful
	 * @param intervalInSeconds  refers to the trackers response parameter interval that
	 * specifies how many seconds to wait until the next announce. If the announce was unsuccessful,
	 * use some arbitrary value.
	 */
	public void setAnnounced(boolean success, int intervalInSeconds) {

		lastAnnounceDate = System.currentTimeMillis();
		wasLastAnnounceSuccess = success;
		nextAnnounceDate = lastAnnounceDate + (1000 * intervalInSeconds);
	}
	
	/**
	 * Sets the tracker id for this tracker. The tracker id is optionally set in a Tracker response
	 * and should be send with future tracker requests (if set).
	 */
	
	public void setTrackerId(String trackerId) {

		this.trackerId = trackerId;
	}
	
	/**
	 * Returns the tracker id previously set by {@link #setTrackerId(String)}. If trackerId was
	 * not set, {@link #getTrackerId()} will return <b>null</b>.
	 */
	public String getTrackerId() {

		return trackerId;
	}

	/**
	 * Returns TRUE if the last announce was successful. If there were no announces, return false.
	 */
	public boolean isWasAnnounceSuccessfull() {

		return wasLastAnnounceSuccess;
	}

	/**
	 * Performs an announce of Torrent (identified by <em>infoHash</em>) to this tracker.<br/>
	 * {@link #lastAnnounceDate}, {@link #nextAnnounceDate}, {@link #wasLastAnnounceSuccess} and
	 * {@link #getTrackerId()} of this tracker are updated accordingly.
	 */
	public TorrentResponse announce(
			Sha1Hash infoHash,
			Sha1Hash peerId,
			int port,
			long uploaded,
			long downloaded,
			long left,
			String ipAddress,
			RequestEvent requestEvent,
			int timeoutInMs) throws IOException {

		if (getUrl().startsWith("http")) {
			TorrentHttpRequest request =
					new TorrentHttpRequest(
							getUrl(),
							infoHash,
							peerId,
							port,
							uploaded,
							downloaded,
							left,
							requestEvent,
							ipAddress,
							getTrackerId());

			return request.execute(timeoutInMs);

		}
		throw new RuntimeException("Not implemented!");
	}

	/**
	 * Returns the last announce date, disregarding whether the last announce was successful.<br/>
	 * If there were no announces, 0 (zero) is returned.
	 */
	public long getLastAnnounceDate() {

		return lastAnnounceDate;
	}

	/**
	 * Returns the last announce data + the specified interval (see {@link #setAnnounced(boolean, int)}.
	 * If there were no announces, 0 (zero) is returned.
	 */
	public long getNextAnnounceDate() {

		return nextAnnounceDate;
	}

	/**
	 * Returns the Tracker's URL.
	 */
	public String getUrl() {

		return url;
	}
}


